package com.bsi.core.validate;

import com.bsi.core.utils.ClassUtil;
import com.bsi.core.validate.handler.DefaultValidateHandler;
import com.bsi.core.validate.handler.ValidateHandler;
import com.bsi.core.validate.rule.ValidateRule;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

/*
 * Author: liusongs
 * Date: 2019/11/5 10:44
 * Content: 校验框架的配置类，handler的默认实例在此注入，本工程下的rule会查找到交给spring容器管理，
 * 其余工程被spring容器管理的rule类都会放入到RuleHandler的list属性用来调用所有rule的check方法
 */
public class ValidateConfiguration implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor, BeanPostProcessor {

    private static ApplicationContext APPLICATIONCONTEXT;

    /*
     *
     * 当sping容器中未有扩展的validateHandler时，会使用默认的defaultValidateHandler处理器来进行处理
     *
     * @return com.bsi.core.validate.handler.ValidateHandler
     * */
    @Bean
    @ConditionalOnMissingBean(value = ValidateHandler.class)
    public ValidateHandler defaultValidateHandler() {
        return new DefaultValidateHandler();
    }

    // 获取所有的rule实现类
    public List<Class> findAllRules(Class clazz) {
        // 利用反射获取所有的rule实现类
        ArrayList<Class> allClassByInterface = ClassUtil.getAllClassByInterface(clazz);
        // 将类放置到spring容器中
        return allClassByInterface;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ValidateConfiguration.APPLICATIONCONTEXT = applicationContext;
    }

    public ValidateRule getRule(Class<? extends ValidateRule> tClass) {
        return APPLICATIONCONTEXT.getBean(tClass);
    }

    /**
     * 扫描本common-core工程下的所有rule接口的继承类，并注入到spring容器中
     * 目的: 如果我们不配这个的话那么本工程的rule类都必须要加上@Componet等注解才能被spring管理，而此工程只是作为一个工具工程，
     * 如果配置这个注解，那么就需要引用此工程的工程需要将扫描也要加到此jar包中，那么就脱离我创建框架的无耦合想法。故自己扫描实现类然后在交给spring管理
     *
     * @param beanDefinitionRegistry
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        // 获取所有validateRule实现类的接口
        List<Class> allRules = findAllRules(ValidateRule.class);
        if (CollectionUtils.isEmpty(allRules)) {
            return;
        }
        // 注入到spring容器中
        for (Class clazz : allRules) {
            //首先将字节对象变成BeanDefinition对象（spring定义的<bean/>标签最终也是封装成这个类）
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
            // 设置为通过类型注入
            beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            beanDefinition.setBeanClass(clazz);
            // 注入到spring容器中
            String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);
            beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
        }
    }

    /**
     * 工厂执行完Init方法后，说明spring的applicationContext容器已经初始化好，就往map里面放入对象，
     * 将所有ValidateRule接口的实现类丢入到validateHandler的接口中的VALIDATEHANDLER_LIST集合中
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ValidateRule) {
            ValidateHandler.VALIDATEHANDLER_LIST.put(beanName, (ValidateRule) bean);
        }
        return bean;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}
